/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.handler.codec;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ByteProcessor;

import java.util.List;

/**
 * A decoder that splits the received {@link ByteBuf}s on line endings.
 * <p>
 * Both {@code "\n"} and {@code "\r\n"} are handled.
 * <p>
 * The byte stream is expected to be in UTF-8 character encoding or ASCII. The current implementation
 * uses direct {@code byte} to {@code char} cast and then compares that {@code char} to a few low range
 * ASCII characters like {@code '\n'} or {@code '\r'}.
 * UTF-8 is not using low range [0..0x7F] byte values for multibyte codepoint representations therefore fully supported by this implementation.
 * <p>
 * For a more general delimiter-based decoder, see {@link DelimiterBasedFrameDecoder}.
 *
 * 把接收到的ByteBuf 根据行尾符,进行切分
 *
 * 可以处理的行尾符: \n , \r\n
 * 这个数据流(byte stream) 只能是 UTF-8 或 ASCII . 当前这个实现类, 直接将byte 转为 char .
 * 然后 将char与 低范围的ASCII码的 '\n' 或 '\r' 作比对 .
 *
 * UTF-8 不使用低范围 [0..0x7F] 字节 来表示多字节代码点 .因此实现类也是支持的 /
 * 这句话好绕 , 没懂..
 * TODO : UTF8 这个点不理解
 *
 * 要了解更多类似 解码器, 可以看 {@link DelimiterBasedFrameDecoder}
 *
 *
 *
 *
 *
 *
 */
public class LineBasedFrameDecoder extends ByteToMessageDecoder {

    /** Maximum length of a frame we're willing to decode.  */
    /** 一帧中,可处理的最大字节数 */
    private final int maxLength;
    /** Whether or not to throw an exception as soon as we exceed maxLength. */
    /** 设置是否要抛出异常 : (默认 false) 只要超出maxLength ,那么就是失败的, 就会抛出异常 */
    private final boolean failFast;
    /**是否将 解码后的帧中 , 删除分隔符*/
    private final boolean stripDelimiter;

    /** True if we're discarding input because we're already over maxLength.  */
    /** 是否丢了数据 (false) , 如果数据达到 maxLength,那么其他的都丢掉 */
    private boolean discarding;
    // 丢掉了的字节数
    private int discardedBytes;

    /** Last scan position. */
    /** 最后扫描的位置 */
    private int offset;

    /**
     * Creates a new decoder.
     * 创建一个 解码器 实例
     * 指定一个帧中 , 可处理的最大字节数
     * @param maxLength  the maximum length of the decoded frame.
     *                   A {@link TooLongFrameException} is thrown if
     *                   the length of the frame exceeds this value.
     */
    public LineBasedFrameDecoder(final int maxLength) {
        this(maxLength, true, false);
    }

    /**
     * Creates a new decoder.
     * @param maxLength  the maximum length of the decoded frame.
     *                   A {@link TooLongFrameException} is thrown if
     *                   the length of the frame exceeds this value.
     * @param stripDelimiter  whether the decoded frame should strip out the
     *                        delimiter or not
     * @param failFast  If <tt>true</tt>, a {@link TooLongFrameException} is
     *                  thrown as soon as the decoder notices the length of the
     *                  frame will exceed <tt>maxFrameLength</tt> regardless of
     *                  whether the entire frame has been read.
     *                  If <tt>false</tt>, a {@link TooLongFrameException} is
     *                  thrown after the entire frame that exceeds
     *                  <tt>maxFrameLength</tt> has been read.
     */
    public LineBasedFrameDecoder(final int maxLength, final boolean stripDelimiter, final boolean failFast) {
        this.maxLength = maxLength;
        this.failFast = failFast;
        this.stripDelimiter = stripDelimiter;
    }

    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        /**对数据解码*/
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    /**
     * Create a frame out of the {@link ByteBuf} and return it.
     *
     * @param   ctx             the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param   buffer          the {@link ByteBuf} from which to read data
     * @return  frame           the {@link ByteBuf} which represent the frame or {@code null} if no frame could
     *                          be created.
     */
    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
        /** 找到字节中的行尾符所有在位置 */
        final int eol = findEndOfLine(buffer);
        if (!discarding) {
            /**
             * 不丢数据
             */
            if (eol >= 0) {
                /** 有行尾符 */
                final ByteBuf frame;
                /** 当前位置, 到行尾符中间的数据长度 */
                final int length = eol - buffer.readerIndex();
                /** 行尾符的长度,有'\r'算两个,没有算一个 */
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;

                if (length > maxLength) {
                    /** 超出最大可读取长度 , 把读位置放到行尾符后*/
                    buffer.readerIndex(eol + delimLength);
                    /** 执行失败逻辑*/
                    fail(ctx, length);
                    return null;
                }

                if (stripDelimiter) {
                    /** 要去掉行尾符*/
                    /** 把当前帧,重置为没有行尾符的帧,之后的都不要了*/
                    frame = buffer.readRetainedSlice(length);
                    /** 原buffer,跳过行尾符*/
                    buffer.skipBytes(delimLength);
                } else {
                    /** 不去掉行尾符*/
                    /** 算上行尾符,重置当前帧,超出行尾符的都不要了*/
                    frame = buffer.readRetainedSlice(length + delimLength);
                }
                /** 返回帧*/
                return frame;
            } else {
                /** 没有行尾符 */
                /** 取数据长度*/
                final int length = buffer.readableBytes();
                if (length > maxLength) {
                    /** 数据长度,超出最大范围 */
                    /** 数据全不要了*/
                    discardedBytes = length;
                    /** 读位置放到写文件,就是没有可读字节了*/
                    buffer.readerIndex(buffer.writerIndex());
                    /** 标识丢数据了*/
                    discarding = true;
                    offset = 0;
                    if (failFast) {
                        /** 要失败 , 执行失败逻辑*/
                        fail(ctx, "over " + discardedBytes);
                    }
                }
                /** 没有行尾符,长度也在符合范围内,那么这个handler就没有什么要处理的了 .
                 * 最后数据不处理,返回null,上层也不处理*/
                return null;
            }
        } else {
            /** 要丢数据*/
            if (eol >= 0) {
                /** 有行尾符 */
                /** 实际数据长度,discardedBytes == 0*/
                final int length = discardedBytes + eol - buffer.readerIndex();
                /** 行尾符的长度,有'\r'算两个,没有算一个 */
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
                /** 写位置放到行尾符后,等于是行尾符前的不再处理了*/
                buffer.readerIndex(eol + delimLength);
                /** 设置丢数据长度 */
                discardedBytes = 0;
                /** 设置丢数据标识 , 标识没丢*/
                /** TODO 这里有个疑问,也没判断maxLength,怎么就标识没丢数据 ? */
                discarding = false;
                if (!failFast) {
                    /** TODO 更看不懂了, 判断不要失败的时候,执行失败逻辑 ?*/
                    fail(ctx, length);
                }
            } else {
                /** 没有行尾符*/
                /** */
                discardedBytes += buffer.readableBytes();
                buffer.readerIndex(buffer.writerIndex());
                // We skip everything in the buffer, we need to set the offset to 0 again.
                offset = 0;
            }
            return null;
        }
    }

    private void fail(final ChannelHandlerContext ctx, int length) {
        fail(ctx, String.valueOf(length));
    }

    private void fail(final ChannelHandlerContext ctx, String length) {
        ctx.fireExceptionCaught(
                new TooLongFrameException(
                        "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')'));
    }

    /**
     * Returns the index in the buffer of the end of line found.
     * 找到行尾符, 并将位置返回
     * Returns -1 if no end of line was found in the buffer.
     * 没找到那么返回-1
     */
    private int findEndOfLine(final ByteBuf buffer) {
        /**
         * 取可读字节长度
         */
        int totalLength = buffer.readableBytes();
        /**
         * 遍历查找行尾符 , ByteProcessor.FIND_LF == '\n'
         */
        int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF);
        if (i >= 0) {
            /**
             * 有找到行尾符
             * 重置offset
             */
            offset = 0;
            /**
             * 再找下 '\n' 前的字符是不是 '\r' ,
             * 是的话, 也算进去
             */
            if (i > 0 && buffer.getByte(i - 1) == '\r') {
                i--;
            }
        } else {
            /** 没有行尾符 , 那么所有都是正常数据
             * 此时 i == -1
             * */
            offset = totalLength;
        }
        return i;
    }
}
